import type {CancelToken, RequestMethod, ResponseType} from 'umi-request';
import Request, {CancelTokenSource, extend} from 'umi-request';

/**
 * 验证ajax请求异常, 是否为网络请求超时异常
 * @param error ajax异常
 */
function validateExpIsTimeout(error: any) {
    const {message, name, request} = error;
    if (
        message.indexOf('timeout of') >= 0 &&
        message.indexOf('exceeded') >= 0 &&
        name === 'RequestError' &&
        request
    ) {
        return true;
    }
    return false;
}

/**
 * 判断ajax响应状态码是否为500
 * @param e ajax异常
 */
function validateExpIsServer500(e: any) {
    if (e.response?.status === 500) {
        console.error(
            `后端服务:[${e.response.url}] HTTP响应: status:[${
                e.response.status
            }] statusText:[${e.response.statusText}], 具体响应数据:${JSON.stringify(
                e.data,
            )}`,
        );
        return true;
    }
    return false;
}

/**
 * 统一异常处理
 * @param e
 */
function errorHandler(e: any) {
    if (Request.isCancel(e)) {
        console.warn(e.message);
    } else if (validateExpIsTimeout(e)) {
        console.error('ajax请求超时异常', e.timeout, e.name, e.message, e.request);
    } else if (validateExpIsServer500(e)) {
        console.log(e.response);
    } else {
        throw e;
    }
}

const request = extend({
    getResponse: true,
    errorHandler,
});

export default request;

export type AjaxRequestParams = {
    /**
     * 只用于文件上传方法 fileUpload
     */
    formData?: FormData,
    /**
     * 请求地址
     */
    url: string;
    /**
     * 提交的数据
     */
    data?: any;
    /**
     * 额外的请求头参数
     */
    headers?: HeadersInit;
    /**
     * 超时时间(单位:毫秒)
     */
    timeout?: number;
    /**
     * 自定义异常处理
     * @param error
     */
    errorHandler?: (error: any) => void;
    /**
     * 响应内容: 常用值值 json, text, blob, formData. 默认:json
     */
    responseType?: ResponseType;
    /**
     * 用于创建可手动取消的ajax请求. 使用 buildCancelTokenSource() 创建, 使用 cancel() 取消
     */
    cancelTokenSource?: CancelTokenSource | null;
    /**
     * 文件下载请求方式(该参数只用于fileDownload方法). get:表示get请求, form:表示post使用表单传参方式请求, body:表示post使用body传参方式请求. 默认:get
     */
    fileDownloadType?: 'get' | 'form' | 'body'
}

export const AjaxUtil = {
    /**
     * 构建cancelToken
     */
    buildCancelTokenSource: function (): CancelTokenSource {
        const CancelToken = Request.CancelToken;
        return CancelToken.source();
    },
    /**
     * 取消指定ajax请求. 参数值由 buildCancelTokenSource() 创建
     * @param cancelTokenSource
     */
    cancel: function (cancelTokenSource: CancelTokenSource) {
        cancelTokenSource.cancel();
    },
    /**
     * 获取指定响应头
     * @param responseHeaders 所有响应头
     * @param headerName 指定响应头名称
     */
    getResponseHeaderValue: function (responseHeaders: Headers, headerName: string) {
        return responseHeaders.get(headerName);
    },
    /**
     * 获取指定响应头,并对结果进行 unescape 操作
     * @param responseHeaders 所有响应头
     * @param headerName 指定响应头名称
     */
    getResponseHeaderValueUnescape: function (responseHeaders: Headers, headerName: string) {
        const headerValue = this.getResponseHeaderValue(responseHeaders, headerName);
        return headerValue ? unescape(headerValue) : headerValue;
    },
    /**
     * 获取指定响应头,并对结果进行 encodeURI 操作
     * @param responseHeaders 所有响应头
     * @param headerName 指定响应头名称
     */
    getResponseHeaderValueEncodeURI: function (responseHeaders: Headers, headerName: string) {
        const headerValue = this.getResponseHeaderValue(responseHeaders, headerName);
        return headerValue ? encodeURI(headerValue) : headerValue;
    },
    /**
     * 获取指定响应头,并对结果进行 encodeURIComponent 操作
     * @param responseHeaders 所有响应头
     * @param headerName 指定响应头名称
     */
    getResponseHeaderValueEncodeURIComponent: function (responseHeaders: Headers, headerName: string) {
        const headerValue = this.getResponseHeaderValue(responseHeaders, headerName);
        return headerValue ? encodeURIComponent(headerValue) : headerValue;
    },
    /**
     * 文件下载
     * @param reqParams 请求参数
     * @param buildFileName 构建文件名的回调参数
     */
    fileDownload: async function (reqParams: AjaxRequestParams, buildFileName: (responseHeaders: Headers) => string) {
        const {fileDownloadType} = reqParams;
        // @ts-ignore
        let resp = null;
        const realReqParams: AjaxRequestParams = {...reqParams, responseType: 'blob'};
        switch (fileDownloadType) {
            case 'get': {
                resp = await this.get(realReqParams);
                break;
            }
            case 'form': {
                resp = await this.postForm(realReqParams);
                break;
            }
            default: {
                resp = await this.get(realReqParams);
                break;
            }
        }
        resp.response.blob().then((blob: any) => {
            // @ts-ignore
            buildFileName(resp.response.headers);
            let blobUrl = window.URL.createObjectURL(blob);
            let fileName = 'test.pdf';
            let btn = document.createElement('a');
            btn.download = fileName;
            btn.href = blobUrl;
            btn.click();
            URL.revokeObjectURL(blobUrl);
            btn.remove()
        });
    },
    /**
     * 文件上传
     * @param reqParams
     */
    fileUpload: function (reqParams: AjaxRequestParams) {
        const {headers = {}, formData} = reqParams;
        const realHeaders = {...headers, "Content-Type": "multipart/form-data"};
        return this.postForm({...reqParams, headers: realHeaders, data: formData})
    },
    /**
     * 发起get请求
     * @param reqParams
     */
    get: function (reqParams: AjaxRequestParams) {
        const {url, timeout, errorHandler, headers, responseType = 'json', data, cancelTokenSource} = reqParams;
        const requestMethod = 'get';
        if (cancelTokenSource) {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                params: data,
                responseType,
                cancelToken: cancelTokenSource.token
            });
        } else {
            return request(url, {method: requestMethod, errorHandler, headers, timeout, params: data, responseType});
        }
    },
    /**
     * 发起post请求(表单传参)
     * @param reqParams
     */
    postForm: function (reqParams: AjaxRequestParams) {
        const {url, timeout, errorHandler, headers = {}, responseType = 'json', data, cancelTokenSource} = reqParams;
        const requestMethod = 'post';
        if (cancelTokenSource) {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers: {...headers, 'Content-Type': 'multipart/form-data'},
                timeout,
                params: data,
                responseType,
                cancelToken: cancelTokenSource.token
            });
        } else {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers: {...headers, 'Content-Type': 'multipart/form-data'},
                timeout,
                params: data,
                responseType
            });
        }
    },
    /**
     * 发起post请求(request body传参)
     * @param reqParams
     */
    postBody: function (reqParams: AjaxRequestParams) {
        const {url, timeout, errorHandler, headers, responseType = 'json', data, cancelTokenSource} = reqParams;
        const requestMethod = 'post';
        if (cancelTokenSource) {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                data: data,
                responseType,
                cancelToken: cancelTokenSource.token
            });
        } else {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                data: data,
                responseType
            });
        }
    },
    /**
     * 发起post请求(request body传参)
     * @param reqParams
     */
    put: function (reqParams: AjaxRequestParams) {
        const {url, timeout, errorHandler, headers, responseType = 'json', data, cancelTokenSource} = reqParams;
        const requestMethod = 'put';
        if (cancelTokenSource) {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                data: data,
                responseType,
                cancelToken: cancelTokenSource.token
            });
        } else {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                data: data,
                responseType
            });
        }
    },
    /**
     * 发起delete请求
     * @param reqParams
     */
    deleteReq: function (reqParams: AjaxRequestParams) {
        const {url, timeout, errorHandler, headers, responseType = 'json', data, cancelTokenSource} = reqParams;
        const requestMethod = 'delete';
        if (cancelTokenSource) {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                params: data,
                responseType,
                cancelToken: cancelTokenSource.token
            });
        } else {
            return request(url, {
                method: requestMethod,
                errorHandler,
                headers,
                timeout,
                params: data,
                responseType
            });
        }
    }
}
